from flask import Blueprint, request, current_app
from flask_jwt_extended import jwt_required, get_jwt_identity
from backend.decorators import admin_required
# 确保从models导入的是我们最终修正的Property模型
from backend.models import db, Property,Footprint
from backend.utils import format_response, api_response
from sqlalchemy import func,asc,desc # <--- 新增: 导入 sqlalchemy 的 func 以使用数据库函数

properties_bp = Blueprint('properties', __name__)

# <--- 新增: 从poi.py借鉴的坐标验证辅助函数，确保一致性
def validate_coordinates(value, min_val, max_val):
    """验证坐标值是否为在有效范围内的浮点数"""
    if value is None:
        return None
    try:
        num = float(value)
        if not (min_val <= num <= max_val):
            raise ValueError
        return num
    except (ValueError, TypeError):
        return None

@properties_bp.route('', methods=['GET'])
@format_response
def get_properties():
    """查询小区数据，支持按区域、商圈、名称、价格范围筛选"""
    query = db.session.query(
        Property,
        func.avg(Footprint.rating).label('avg_rating')  # 2. 计算 rating 的平均值，命名为 avg_rating
    ).outerjoin(
        Property.followers  # 'followers' 是您在 Property 模型中定义的关系名
    ).group_by(Property.id)  # 3. 按房源ID分组，确保平均分是针对每个房源的

    # 【核心修改】这里的 .filter() 调用必须使用模型中定义的真实属性名
    if name := request.args.get('name'):
        # 前端发来 'name' 参数，但在数据库查询时要用 'community' 字段
        query = query.filter(Property.community.ilike(f'%{name}%'))
    if district := request.args.get('district'):
        # 前端发来 'district' 参数，查询时用 'region' 字段
        query = query.filter(Property.region.ilike(f'%{district}%'))
    if sub_district := request.args.get('sub_district'):
        # 前端发来 'sub_district' 参数，查询时用 'business_district' 字段
        query = query.filter(Property.business_district.ilike(f'%{sub_district}%'))

    # 价格区间筛选 (基于平均单价)
    try:
        if price_min := request.args.get('price_min', type=float):
            # 查询时用 'unit_price_avg' 字段
            query = query.filter(Property.unit_price_avg >= price_min)
        if price_max := request.args.get('price_max', type=float):
            # 查询时用 'unit_price_avg' 字段
            query = query.filter(Property.unit_price_avg <= price_max)
    except (ValueError, TypeError):
        return api_response(code=400, message="无效的价格参数")

    sw_lat_req = request.args.get('sw_lat')
    center_lat_req = request.args.get('center_lat')

    # 拉框查询和点半径查询是互斥的，优先处理拉框查询
    if sw_lat_req:
        sw_lat = validate_coordinates(sw_lat_req, -90, 90)
        sw_lng = validate_coordinates(request.args.get('sw_lng'), -180, 180)
        ne_lat = validate_coordinates(request.args.get('ne_lat'), -90, 90)
        ne_lng = validate_coordinates(request.args.get('ne_lng'), -180, 180)

        if not all([sw_lat, sw_lng, ne_lat, ne_lng]):
            return api_response(code=400, message="无效的拉框查询坐标参数 (sw_lat, sw_lng, ne_lat, ne_lng)")

        query = query.filter(
            Property.latitude >= sw_lat,
            Property.latitude <= ne_lat,
            Property.longitude >= sw_lng,
            Property.longitude <= ne_lng
        )
    # --- <--- 新增: 点半径查询，实现与poi.py一致 ---
    elif center_lat_req:
        center_lat = validate_coordinates(center_lat_req, -90, 90)
        center_lng = validate_coordinates(request.args.get('center_lng'), -180, 180)

        if not all([center_lat, center_lng]):
            return api_response(code=400, message="无效的中心点坐标参数 (center_lat, center_lng)")

        try:
            # 默认半径为5000米 (5公里)，并进行验证
            radius = request.args.get('radius', 5000, type=int)
            if not (0 < radius <= 100000):  # 限制半径在0到100公里之间
                raise ValueError
        except (ValueError, TypeError):
            return api_response(code=400, message="半径(radius)必须是 0-100000 米之间的整数")

        # 使用 ST_DistanceSphere 计算球面距离
        # 注意: Property模型中的经纬度是Numeric类型，需要强制转换为Float或Double才能用于ST_MakePoint
        point = func.ST_MakePoint(func.cast(Property.longitude, db.Float), func.cast(Property.latitude, db.Float))
        center_point = func.ST_MakePoint(center_lng, center_lat)
        distance = func.ST_DistanceSphere(point, center_point)

        query = query.filter(distance <= radius)

        # --- [核心修正] 排序逻辑 ---
        sort_by = request.args.get('sort_by', 'id')
        order = request.args.get('order', 'asc').lower()

        valid_columns = {  # 允许排序的、Property模型中真实存在的字段
            'id', 'community', 'region', 'business_district', 'listing_year', 'listing_month',
            'unit_price_avg', 'unit_price_median', 'unit_price_min', 'unit_price_max',
            'total_price_avg', 'total_price_median', 'total_price_min', 'total_price_max'
        }

        # 1. 优先处理特殊的、动态计算的排序字段 'avg_rating'
        if sort_by == 'avg_rating':
            if order == 'desc':
                # 直接使用字符串别名 'avg_rating' 进行排序
                query = query.order_by(desc('avg_rating').nullslast())
            else:
                query = query.order_by(asc('avg_rating').nullsfirst())
        # 2. 然后再处理模型中真实存在的字段
        elif sort_by in valid_columns:
            sort_column = getattr(Property, sort_by)  # 这行代码现在是安全的
            if order == 'desc':
                query = query.order_by(desc(sort_column))
            else:
                query = query.order_by(asc(sort_column))

    # 分页
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)
    pagination = query.paginate(page=page, per_page=min(per_page, 100), error_out=False)
    results = []
    for prop, avg_rating in pagination.items:
        prop_dict = prop.to_dict()
        # 5. 将计算出的均分添加到返回的字典中
        prop_dict['avg_rating'] = float(avg_rating) if avg_rating is not None else None
        results.append(prop_dict)

    # to_dict_summary() 方法已经处理好了Python属性到JSON键的转换，所以这里无需修改
    return {
        "list": results,
        "pagination": {
            "current_page": page, "per_page": per_page, "total_items": pagination.total,
            "total_pages": pagination.pages
        }
    }, 200


# 以下为管理员CRUD接口，其实现方式具有动态性，无需修改
@properties_bp.route('', methods=['POST'])
@jwt_required()
@admin_required
@format_response
def create_property():
    data = request.json
    data.pop('id', None)

    # --- <--- 新增: 按您的要求打印接收到的JSON ---
    print("--- Received JSON data for creating property ---")
    print(data)
    print("---------------------------------------------")

    if not data:
        return api_response(code=400, message="请求体不能为空")

    try:
        # **data 会将JSON的键值对直接传递给模型构造函数，只要JSON的键和模型属性名一致即可
        listing_month = float(data.get('listing_month')) if data.get('listing_month') is not None else None
        new_prop = Property(
            community=data.get('community'),
            region = data.get('region'),
            business_district = data.get('business_district'),
            listing_year = int(data.get('listing_year')),
            listing_month=int(data.get('listing_month')),
            unit_price_avg = float(data.get('unit_price_avg')),
            unit_price_median = float(data.get('unit_price_median')),
            unit_price_min = float(data.get('unit_price_min')),
            unit_price_max = float(data.get('unit_price_max')),
            total_price_avg = float(data.get('total_price_avg')),
            total_price_median = float(data.get('total_price_median')),
            total_price_min = float(data.get('total_price_min')),
            total_price_max = float(data.get('total_price_max')),
            longitude = float(data.get('longitude')),
            latitude = float(data.get('latitude'))

        )
        db.session.add(new_prop)
        db.session.commit()
        return new_prop.to_dict(), 201

    except (ValueError, TypeError) as e:
        return api_response(code=400, message=f"无效的数据格式: {e}")
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error creating Property: {e}")
        return api_response(code=500, message=f"创建小区数据时出错: {e}")


@properties_bp.route('/<int:prop_id>', methods=['PUT'])
@jwt_required()
@admin_required
@format_response
def update_property(prop_id):
    prop = Property.query.get_or_404(prop_id)
    data = request.json
    try:
        # hasattr 会检查模型实例是否存在某个属性，因此也是动态匹配的
        for key, value in data.items():
            if hasattr(prop, key):
                if key == 'listing_month' and value is not None:
                    value = float(value)
                setattr(prop, key, value)
        db.session.commit()
        return prop.to_dict(), 200
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error updating Property {prop_id}: {e}")
        return api_response(code=500, message=f"更新小区数据时出错: {e}")


@properties_bp.route('/<int:prop_id>', methods=['DELETE'])
@jwt_required()
@admin_required
@format_response
def delete_property(prop_id):
    prop = Property.query.get_or_404(prop_id)
    try:
        db.session.delete(prop)
        db.session.commit()
        return {'message': '小区数据删除成功'}, 200
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error deleting Property {prop_id}: {e}")
        return api_response(code=500, message=f"删除小区数据时出错: {e}")

# [新增] 获取单个房源所有公开“足迹”（作为评论）的路由
@properties_bp.route('/<int:prop_id>/reviews', methods=['GET'])
@format_response
def get_property_reviews(prop_id):
    """获取指定房源的所有有效足迹（作为公开评论）"""
    # 验证房源是否存在
    if not Property.query.get(prop_id):
        return api_response(code=404, message="Property not found")

    # 查询Footprint表，找到所有与该房源ID关联的记录。
    # 为了只显示有效的“评论”，我们筛选出那些填写了笔记(notes)或给出了评分(rating)的足迹。
    # 这样可以过滤掉用户仅仅是点击收藏而没有做任何评价的记录。
    # 按时间降序排列，最新的评论在最前面。
    reviews = Footprint.query.filter(
        Footprint.property_id == prop_id,
        db.or_(Footprint.notes.isnot(None), Footprint.rating.isnot(None))
    ).order_by(Footprint.added_at.desc()).all()

    # 将每个Footprint对象转换为字典格式返回给前端。
    # 注意：这要求您的Footprint模型中的to_dict()方法能够包含用户信息(例如用户名)。
    return [review.to_dict_for_public_review() for review in reviews], 200